package qc.common.core.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * QMS数值工具，提供2个数值是否相等方法用于判断数据值是否有变化
 *
 * @author QuCheng Tech
 * @since 2025/8/22
 */
public class QmsNumericUtil {
    /**
     * 使用全局默认设置的分辨力
     */
    private static final double DEFAULT_RESOLUTION = 0.00000001f;

    /**
     * 判断2个Double是否相等，根据传入的最小分辨力，如果没有传入使用全局默认设置的分辨力
     *
     * @param num1       数值1
     * @param num2       数值2
     * @param resolution 分辨力
     * @return boolean
     * @author QuCheng Tech
     * @since 2025/8/22
     */
    public static boolean isEqual(Double num1, Double num2, Double resolution) {
        //System.out.println("数值相等比较，根据最小分辨力[" + resolution + "]，比较[" + num1 + "]和[" + num2 + "]");

        //2个数字其中只有一个为null或者为NaN，判定为不相等
        if (num1 == null || Double.isNaN(num1)) {
            //num1为null或NaN
            if (num2 == null || Double.isNaN(num2))//2个数字均为null或NaN，判定为相等
                return true;
            else
                return false;
        } else {
            //num1不为null或NaN
            if (num2 == null || Double.isNaN(num2))
                return false;
            else {
                //2个数字均不为null或者NaN，判断数值是否超过分辨力
                double r = getResolutionValue(resolution);

                //计算2个数值的差值绝对值
                double numDiff = Math.abs(num1.doubleValue() - num2.doubleValue());

                //2025-10-2，实测结果：使用代码  diff < r 根据最小分辨力[0.01]，比较[872.65]和[872.64]，计算出差值[0.009999999999990905]，是否相等结果为[true]
                //计算出差值[0.009999999999990905] < 最小分辨力[0.01]；判定为2个数值相等，实际是相差0.01，预期结果是不相等

                //由于double类型数值运算后会存在小数点后多位有变化，需要排除小数点后误差因素；排除误差因素的方法如下：
                //设置一个误差值，误差值 = 最小分辨力 * 0.1 即最小分辨力小数点再往后一位；调整为：误差值 = 最小分辨力 * 0.5 即取最小分辨力的一半，默认为五舍五入 
                double epsilon = r * 0.5f;

                //返回比较结果：2数的差值＜误差值 判定为相等；
                boolean isEqual = numDiff < epsilon;

                //System.out.println("数值相等比较，根据最小分辨力[" + r + "]，比较[" + num1 + "]和[" + num2 + "]，两数差值[" + numDiff + "]，与误差值[" + epsilon + "]，是否相等结果为[" + isEqual + "]");

                return isEqual;
            }
        }
    }

    /**
     * 获取分辨力值，如果传入的无效使用默认值
     *
     * @param resolution 分辨力
     * @return double
     * @author QuCheng Tech
     * @since 2025/10/3
     */
    static double getResolutionValue(Double resolution) {
        //如果传入的分辨力无效，使用默认；判断无效的方法：为null、为NaN、为负值
        if (resolution != null && resolution.isNaN() == false && resolution.isInfinite() == false && resolution.doubleValue() > 0.0f)
            return resolution.doubleValue();

        return DEFAULT_RESOLUTION;
    }

    /**
     * 获取按分辨力保留精度的数据值，如果数据值本身无效返回自身
     *
     * @param value      数据值
     * @param resolution 分辨力
     * @return java.lang.Double
     * @author QuCheng Tech
     * @since 2025/10/3
     */
    public static Double getScaleValue(Double value, Double resolution) {
        if (value != null && value.isNaN() == false && value.isInfinite() == false) {
            //BigDecimal构造函数需要传入string，传入double类型时会导致处理失效
            //如果有数据值，数据值对精度保留处理，按最小分辨力保留到指定精度
            BigDecimal bigDecimal = new BigDecimal(String.valueOf(value.doubleValue()));
            //设置保留的精度小数位，取分辨力的小数点后的总位数，BigDecimal构造函数需要传入string
            //int scale = new BigDecimal(getResolutionValue(resolution)).scale();
            //int scale = new BigDecimal(String.valueOf(getResolutionValue(resolution))).scale();
            //如果分辨力小于1表示精确到小数点后几位，如果分辨力≥1均当做保留整数
            double r = getResolutionValue(resolution);
            int scale = 0x0;
            if (r < 1.0f && isEqual(r, 1.0, 0.1) == false)
                scale = new BigDecimal(String.valueOf(r)).scale();

            //System.out.println("数值按精度保留处理，分辨力[" + resolution + "]，保留精度位数[" + scale + "]");

            //返回按保留精度做四舍五入后的double值
            return bigDecimal.setScale(scale, RoundingMode.HALF_UP).doubleValue();
        }

        return value;
    }
}
